home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Magnum One
/
Magnum One (Mid-American Digital) (Disc Manufacturing).iso
/
d18
/
totdoc.arc
/
CHAPT16.TXT
< prev
next >
Wrap
Text File
|
1991-02-10
|
17KB
|
367 lines
OOP
Principles
"The human mind treats a new idea the way the body treats a strange
protein; it rejects it."
P. B. Medawar, Biologist
Part 2 of the User's manual explains how, by using object oriented
programming, you can customize the Toolkit to meet your very specific
needs. In this chapter, some of the main features of OOP extensibility
are explained. If you are an experienced OOPer, you may want to skip
this chapter and proceed with the Toolkit specific text.
If you are new to OOP, be sure to read the Object Primer starting on
page 3.1 before you look at this chapter.
Understanding Inheritance
One of the most powerful and alluring aspects of object oriented pro-
gramming is the ability to modify and expand an object without changing
any of the object's source code. What's more, it's easy to do! The
basic approach is to create an object which inherits all the properties
of another object, i.e. you clone the object (sorry Compaq). You can
then change some of the properties of the cloned object so that it
functions the way you want it to.
This feature allows you to enhance and improve the Toolkit without
changing a single line of Toolkit source code. That way, when we
release a new and improved version, you don't have to wrestle with two
sets of source code - the code that you changed and the code that we
changed. All you need to do is "plug in" the new Toolkit and your
objects will automatically inherit the new features added to the Tool-
kit.
To illustrate object inheritance, let's solve a practical problem.
Imagine that you use the EquipOBJ object, but need to enhance it so
that it will report if a CD-ROM is installed. Rather than change the
EquipOBJ object, the approach is to create a new object which inherits
all the properties of EquipOBJ, and add the new CD-ROM method to it.
You may recall from chapter 3: Toolkit Basics that declaring an object
is very similar to declaring a record. To declare a object that inher-
its the properties of another object, you specify the name of the par-
ent object in parentheses after the object keyword. The following code
fragment declares a new object type, NewEquipOBJ, which inherits all
the properties of EquipOBJ:
NewEquipOBJ = object (EquipOBJ)
end; {NewEquipOBJ}
16-2 Extending the Toolkit
--------------------------------------------------------------------------------
If you create an object which is a descendant of a Toolkit object, you
must define two special methods known as the CONSTRUCTOR and the
DESTRUCTOR. Furthermore, these should be named INIT and DONE, respec-
tively. These special methods instruct Turbo Pascal to use special
memory techniques when creating and disposing of the object. The
NewEquipOBJ object declaration must be expanded to include the con-
structor and destructor methods as follows:
NewEquipOBJ = object (EquipOBJ)
constructor Init;
destructor Done;
end; {NewEquipOBJ}
Now we can add the new CD-ROM method to the object declaration. Assum-
ing that the new method is just a boolean function, it would be
declared as follows:
NewEquipOBJ = object (EquipOBJ)
constructor Init;
function CDROM: boolean;
destructor Done;
end; {NewEquipOBJ}
As well as adding new methods to the object, you can override or change
existing methods. For example, the GameAdapter function inherited from
EquipOBJ can be replaced with a new method by expanding the object
declaration as follows:
NewEquipOBJ = object (EquipOBJ)
constructor Init;
function CDROM: boolean;
function GameAdapter: boolean;
destructor Done;
end; {NewEquipOBJ}
Although you have only declared NewEquipOBJ with four methods, it
actually includes all the data and methods it has inherited from Equi-
pOBJ. The following table compares the data and methods declared in
EquipOBJ, with the data and methods in NewEquipOBJ. The non-bold items
are inherited.
EquipOBJ NewEquipOBJ
vMainInfo: integer; vMainInfo: integer;
vComputerID: byte; vComputerID: byte;
vRomDate: string[8]; vRomDate: string[8];
constructor Init; constructor Init;
function ComputerID:byte; function ComputerID:byte;
function ParallelPorts:byte; function ParallelPorts:byte;
function SerialPorts:byte; function SerialPorts:byte;
function FloppyDrives:byte; function FloppyDrives:byte;
OOP Principles 16-3
--------------------------------------------------------------------------------
function ROMDate:string; function ROMDate:string;
function SerialPrinter:boolean; function SerialPrinter:boolean;
function MathChip:boolean; function MathChip:boolean;
function GameAdapter:boolean; function GameAdapter:boolean;
destructor Done; function CDROM:boolean;
destructor Done;
Note: an inherited object may also include data items, provided
the identifiers (or variable names) are different from the ones
inherited from the parent object.
The object declaration defines a new object type and should be located
in the TYPE declaration section of your program or unit. The corre-
sponding detail of each object method must be listed either in the body
of your program, or in the implementation section of a unit.
Listed below is a unit, EXTDEM1.PAS, which fully defines the new object
NewEquipOBJ. One of the important points to note is that the construc-
tor and destructor methods must call the associated constructor and
destructor from the parent method. This ensures that the inherited data
is initialized and disposed of properly. These important statements are
highlighted in bold in the listing.
Unit ExtDem1;
INTERFACE
Uses DOS,CRT, totSYS;
TYPE
NewEquipOBJ = object (EquipOBJ)
constructor Init;
function CDROM: boolean;
function GameAdapter: boolean;
destructor Done;
end; {NewEquipOBJ}
IMPLEMENTATION
constructor NewEquipOBJ.Init;
{}
begin
EquipOBJ.Init;
end; {NewEquipOBJ.Init}
16-4 Extending the Toolkit
--------------------------------------------------------------------------------
function NewEquipOBJ.CDROM:boolean;
{If you know how to do this - please tell us!
For now, we'll assume one isn't installed}
begin
CDROM := false;
end; {NewEquipOBJ.CDROM}
function NewEquipOBJ.GameAdapter:boolean;
{}
begin
GameAdapter := paramstr(1) = '/G';
end; {NewEquipOBJ.GameAdapter}
destructor NewEquipOBJ.Done;
{}
begin
EquipOBJ.Done;
end; {NewEquipOBJ.Done}
end.
The body of the CD-ROM and GameAdapter methods would normally contain
your custom code. In this case, their value is limited. But you get the
idea!
Take a look at the object hierarchy diagrams at the beginning of the
Flash Cards. They show the Toolkit's parent-sibling object relation-
ships. For example, ScrollWinOBJ inherits MoveWinOBJ, which, in turn,
inherits WinOBJ.
Mastering Extensibility
All the methods in the EquipOBJ object, discussed in the last section,
are known as static methods. Static methods are usually independent
methods, i.e. methods which do not call other methods in the object.
For example, the GameAdapter method has no relationship with the Math-
Chip method or any other EquipOBJ method.
Inheritance becomes somewhat more complicated when methods call other
methods from the same object. The problem is best illustrated by exam-
ple. The unit listing below contains an object, PrintOBJ, which is used
to print strings or integers to the printer connected to LPT1. The
objective is to create a descendant object which can print to any
printer port.
Unit BadPrint;
INTERFACE
Uses DOS,CRT, totSTR;
OOP Principles 16-5
--------------------------------------------------------------------------------
TYPE
PrintOBJ = object
constructor Init;
procedure PrintChar(Ch:char);
procedure PrintStr(Str:string);
procedure PrintInt(Int:integer);
destructor Done;
end; {PrintOBJ}
IMPLEMENTATION
constructor PrintOBJ.Init;
{no data to initialize}
begin
end; {PrintOBJ.Init}
procedure PrintOBJ.PrintChar(Ch:char);
{}
var
Lst: text;
begin
Assign(Lst,'LPT1');
Rewrite(Lst);
Write(Lst,Ch);
Close(Lst);
end; {PrintOBJ.PrintChar}
procedure PrintOBJ.PrintStr(Str:string);
{}
var I : integer;
begin
for I := 1 to length(Str) do
PrintChar(Str[I]);
end; {PrintOBJ.PrintStr}
procedure PrintOBJ.PrintInt(Int:integer);
{}
var I:integer; Str:string;
begin
Str := IntToStr(Int);
for I := 1 to length(Str) do
PrintChar(Str[I]);
end; {PrintOBJ.PrintInt}
destructor PrintOBJ.Done;
{no data to dispose of}
begin
end; {PrintOBJ.Done}
end.
16-6 Extending the Toolkit
--------------------------------------------------------------------------------
The two main methods are PrintStr and PrintInt, but these methods both
call the method PrintChar to print each individual character. (By
design, the code is simplistic; just remember the main purpose is to
teach you about extensibility, not how to write to the printer!)
PrintChar is the root of the problem because it is hard-coded to print
to LPT1. If you are getting into the OOP groove, and you're from Cali-
fornia, you might say: "No sweat, DUDE! Create a descendant object and
replace the PrintChar method. Gnarly!" It's the right idea, but in this
case it won't work.
If you create a descendant object, NewPrintOBJ, and replace the Print-
Char method, the object will inherit the PrintStr and PrintInt methods.
However, when you call PrintStr or PrintInt, they will in turn call
THEIR object's version of PrintChar, i.e. PrintOBJ.PrintChar not New-
PrintOBJ.PrintChar. One solution is to also replace the PrintStr and
PrintInt methods, but then you have rewritten the entire object without
taking advantage of inheritance!
The OOP solution lies in virtual methods. If the PrintChar method in
PrintOBJ is declared as virtual, Turbo Pascal will manage the situation
very differently. A method is identified as virtual by adding the key-
word virtual at the end of the method declaration. The following code
fragment shows the PrintOBJ object declared with a virtual method:
PrintOBJ = object
constructor Init;
procedure PrintChar(Ch:char); virtual;
procedure PrintStr(Str:string);
procedure PrintInt(Int:integer);
destructor Done;
end; {PrintOBJ}
The virtual keyword instructs Turbo Pascal to late bind the PrintChar
method. That is, the compiler implements a special linking method which
ensures that descendant objects, like NewPrintOBJ, can redefine any
virtual methods. The redefined version of the method will always be
called even by inherited methods. For example, the following code
defines a descendant of PrintOBJ (assuming PrintOBJ used the virtual
keyword):
NewPrintOBJ = object (PrintOBJ)
constructor Init;
procedure PrintChar(Ch:char); virtual;
destructor Done;
end; {NewPrintOBJ}
Now if the inherited method NewPrintOBJ.PrintStr is called, Turbo Pas-
cal is smart enough to ensure that PrintStr calls NewPrintOBJ.Print-
Char, and not PrintOBJ.PrintChar. All you would have to do is customize
PrintChar to print to any port and the problem is solved. The on-disk
demo file, EXTDEM2.PAS, includes the full solution for this example.
OOP Principles 16-7
--------------------------------------------------------------------------------
Note: if an object includes virtual methods, the object must have
a constructor and destructor. In addition, if a method is declared
as virtual, all descendant methods with the same name must also be
declared virtual, and they must have identical passed parameters.
The down side of virtual methods is that they will always be linked
into your EXE program code whether the methods are called or not. If
this were not so, there would be good justification for making every
method virtual. As it is, the Toolkit only makes methods virtual if
they are likely to be accessed in some descendant object. It's a trade-
off between complete flexibility and program code size.
You may have heard or read about polymorphism. Polymorphism allows sim-
ilar objects to execute different code when the same call is made to
them from some independent routine. It is virtual methods which provide
Turbo Pascal objects with polymorphism. One of the best Toolkit exam-
ples of polymorphism is in the totIO units. The FormOBJ object, which
controls all the input fields, knows that every input field object has
the methods Display, Select, ProcessKey and Suspend. Any object which
is created as a descendant from BaseIOOBJ will inherit these methods,
and can be managed by FormOBJ. All FormOBJ needs to do is call one of
these methods. (Actually, there are a few more methods than listed, but
the theory still holds.) In chapter 20: Extending Input Fields the full
power of polymorphism is explored.
Know Your Ancestor!
As you have probably assessed, you need to know some details about the
Toolkit objects before you can start to extend them. You need to know
about the methods and the data that will be inherited. Its a good idea
to print the interface section of each unit, as this provides all the
important information about each object's data and methods.
This concludes the brief explanation of OOP. Remember that the text is
aimed at helping you to extend the Toolkit. You should consider re-
reading the discussion of object-oriented programming in the Turbo Pas-
cal's User's Guide. It might make a lot more sense now! In addition, it
covers, in much greater depth, some of the principles introduced in
this chapter.